package cn.bellychang.mxbean;

import cn.bellychang.util.AnnotationScanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ChangLiang
 * @date 2020/3/20
 */
@Component
public class ControllerJmxReporter {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Value("${server.context-path}")
    private String contextPath;

    public String collect() {
        List<Class<?>> list = new ArrayList<>();

        List<Class<?>> restControllerList = AnnotationScanUtil.getClassListByAnnotation("cn.bellychang",
                RestController.class);
        list.addAll(restControllerList);
        List<Class<?>> controllerList = AnnotationScanUtil.getClassListByAnnotation("cn.bellychang", Controller.class);
        list.addAll(controllerList);

        List<ControllerReporterEntity> all = new ArrayList<>();
        for (Class<?> aClass : list) {
            RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
            List<ControllerReporterEntity> controllerReporterEntities = new ArrayList<>();
            if (requestMapping != null) {
                controllerReporterEntities = generateEntity(aClass, requestMapping);
            } else {
                PostMapping postMapping = aClass.getAnnotation(PostMapping.class);
                if (postMapping != null) {
                    controllerReporterEntities = generateEntity(aClass, postMapping);
                }
            }
            all.addAll(controllerReporterEntities);
        }
        return JSON.toJSONString(all);
    }


    private List<ControllerReporterEntity> generateEntity(Class<?> aClass, RequestMapping annotation) {
        return getControllerReporterEntities(aClass, annotation.value());
    }

    private List<ControllerReporterEntity> generateEntity(Class<?> aClass, PostMapping annotation) {
        return getControllerReporterEntities(aClass, annotation.value());
    }

    private List<ControllerReporterEntity> getControllerReporterEntities(Class<?> aClass, String[] value) {
        List<ControllerReporterEntity> result = new ArrayList<>();

        String controllerUrl = "";
        if (value != null && value.length > 0) {
            controllerUrl = value[0];
        }
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            ControllerReporterEntity entity = new ControllerReporterEntity();
            //方法名、post还是get
            RequestMapping mapping = method.getAnnotation(RequestMapping.class);
            if (mapping != null) {
                String interfaceName = mapping.value()[0];
                RequestMethod requestMethod;
                if (mapping.method() != null && mapping.method().length != 0) {
                    requestMethod = mapping.method()[0];
                } else {
                    requestMethod = RequestMethod.POST;
                }
                entity.setMethod(requestMethod.name());
                entity.setUrl(new StringBuilder().append(contextPath).append("/").append(controllerUrl).append("/").append(interfaceName).toString().replace("//", "/"));

                // request param
                List<ControllerArg> list = new ArrayList<>();

                Parameter[] params = method.getParameters();

                for (Parameter param : params) {
                    ControllerArg arg = new ControllerArg();

                    String paramType = param.getType().getName();
                    String paramName = param.getName();
                    if (!param.getType().isPrimitive() && !param.getType().isInterface() && !isWrapClass(param.getType())) {
                        try {
                            // fastjson try to parse a null object, need handle
                            paramName = JSON.toJSONString(param.getType().newInstance(),
                                    SerializerFeature.WriteMapNullValue);
                        } catch (InstantiationException | IllegalAccessException e) {
                            logger.error("实例化接口参数类型格式出错" + e.getMessage(), e);
                            e.printStackTrace();
                        }
                    }

                    arg.setType(paramType);
                    arg.setValue(paramName);

                    list.add(arg);
                }
                entity.setRequest(list);

                // response type
                ControllerArg returnArg = new ControllerArg();

                Class retType = method.getReturnType();
                Object retObj = null;
                if (!retType.isPrimitive() && !retType.isInterface() && !isWrapClass(retType)) {
                    try {
                        retObj = retType.newInstance();
                    } catch (Exception e) {
                        e.printStackTrace();
                        logger.error("实例化返回参数对象报错：" + e.getMessage(), e);
                    }
                }
                returnArg.setType(retType.getTypeName());
                returnArg.setValue(JSON.toJSONString(retObj, SerializerFeature.WriteMapNullValue));
                entity.setResponse(returnArg);
            }
            result.add(entity);
        }
        return result;
    }

    private boolean isWrapClass(Class clz) {
        try {
            return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return false;
        }
    }
}
